package com.efeichong.project.service.impl;

import com.alibaba.druid.util.StringUtils;
import com.efeichong.common.PageData;
import com.efeichong.common.domain.ResponseData;
import com.efeichong.common.domain.entity.redis.LoginUser;
import com.efeichong.common.domain.entity.redis.VerifyCode;
import com.efeichong.common.domain.repository.redis.LoginUserRepository;
import com.efeichong.common.domain.repository.redis.VerifyCodeRepository;
import com.efeichong.common.exception.CustomException;
import com.efeichong.common.security.LoginContextHolder;
import com.efeichong.common.utils.OptionEmptyFunction;
import com.efeichong.common.utils.VerifyCodeUtils;
import com.efeichong.jpa.JExample;
import com.efeichong.project.domain.dao.db.SysDeptDao;
import com.efeichong.project.domain.dao.db.SysMenuDao;
import com.efeichong.project.domain.dao.db.SysUserDao;
import com.efeichong.project.domain.entity.db.SysDept;
import com.efeichong.project.domain.entity.db.SysMenu;
import com.efeichong.project.domain.entity.db.SysUser;
import com.efeichong.project.domain.vo.LoginVo;
import com.efeichong.project.domain.vo.SysMenuVo;
import com.efeichong.project.domain.vo.UserInfoVo;
import com.efeichong.project.domain.vo.UserQueryVo;
import com.efeichong.project.service.SysUserService;
import com.efeichong.uuid.UUIDUtils;
import com.google.common.base.Stopwatch;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @author
 * @date 2020/11/25
 * @description 用户
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class SysUserServiceImpl implements SysUserService {

    private final SysUserDao sysUserDao;
    private final LoginUserRepository loginUserRepository;
    private final VerifyCodeRepository verifyCodeRepository;
    private final AuthenticationManager authenticationManager;
    private final SysMenuDao menuDao;
    private final BCryptPasswordEncoder encoder;
    private final SysDeptDao sysDeptDao;

    /**
     * 通过id查询
     *
     * @param id
     * @return
     */
    @Override
    public UserInfoVo getInfo(Long id) {
        LoginUser loginUser = LoginContextHolder.getLoginUserOrElseThrow();
        if (id != null) {
            return sysUserDao.selectById(id, UserInfoVo.class, "deptVos", "roleVos", "postVos", "password").orElseThrow(OptionEmptyFunction.create());
        } else {
            return sysUserDao.selectById(loginUser.getId(), UserInfoVo.class, "roleIds", "postIds", "deptIds", "password").orElseThrow(OptionEmptyFunction.create());
        }
    }

    /**
     * 分页查询
     *
     * @param userQueryVo
     * @return
     */
    @Override
    public PageData selectByPage(UserQueryVo userQueryVo) {
        Stopwatch started = Stopwatch.createUnstarted();
        started.start();
        JExample<SysUser> example = new JExample();
        example.initExample(userQueryVo);
        example.groupBy("id");
        JExample<SysUser>.Criteria criteria = example.and();
        if (userQueryVo.getDeptId() != null) {
            SysDept parent = sysDeptDao.selectById(userQueryVo.getDeptId()).get();
            List<SysDept> deptChildren = parent.getDeptChildren();
            deptChildren.add(parent);
            criteria.andIn("sysDepts", deptChildren);
        }
        started.stop();
        long elapsed = started.elapsed(TimeUnit.MILLISECONDS);
        return sysUserDao.selectByPage(example, UserInfoVo.class, "roleIds", "postIds", "deptIds");
    }

    /**
     * 新增
     *
     * @param userInfoVo
     * @return
     */
    @Transactional
    @Override
    public void insertSysUser(UserInfoVo userInfoVo) {
        SysUser sysUser = userInfoVo.toPo(SysUser.class);
        if (userInfoVo.getParentId() != null) {
            SysUser parent = sysUserDao.selectById(userInfoVo.getParentId()).get();
            sysUser.setParent(parent);
            List<SysUser> parents = new ArrayList<>();
            while (parent != null) {
                SysUser parentShip = new SysUser();
                parentShip.setId(parent.getId());
                parents.add(parentShip);
                parent = parent.getParent();
            }
            sysUser.setUserParents(parents);
        }
        sysUser.setPassword(encoder.encode(DigestUtils.md5Hex(userInfoVo.getPassword())));
        sysUserDao.save(sysUser);
    }

    /**
     * 修改
     *
     * @param userInfoVo
     * @return
     */
    @Transactional
    @Override
    public void updateSysUser(UserInfoVo userInfoVo) {
        if (userInfoVo.getId() == null) {
            throw new CustomException("主键为必传参数");
        }
        SysUser sysUser = userInfoVo.toPo(SysUser.class);
        if (userInfoVo.getParentId() != null) {
            SysUser parent = sysUserDao.selectById(userInfoVo.getParentId()).get();
            sysUser.setParent(parent);
            List<SysUser> parents = new ArrayList<>();
            while (parent != null) {
                SysUser parentShip = new SysUser();
                parentShip.setId(parent.getId());
                parents.add(parentShip);
                parent = parent.getParent();
            }
            sysUser.setUserParents(parents);
        }
        sysUser.setPassword(null);
        sysUserDao.save(sysUser);
        System.out.println();
    }


    /**
     * 批量删除
     *
     * @param ids
     * @return
     */
    @Transactional
    @Override
    public void deleteByIds(List<Long> ids) {
        sysUserDao.deleteAllByIds(ids);
    }

    /**
     * 删除
     *
     * @param id
     * @return
     */
    @Transactional
    @Override
    public void deleteById(Long id) {
        sysUserDao.deleteById(id);
    }

    /**
     * 账号密码登陆接口
     *
     * @param loginVo
     * @return
     */
    @Override
    public ResponseData login(LoginVo loginVo) {
        Optional<VerifyCode> optional = verifyCodeRepository.findById(loginVo.getCodeId());
        if (!optional.isPresent() || !StringUtils.equalsIgnoreCase(loginVo.getCode(), optional.get().getCode())) {
            throw new CustomException("验证码错误");
        }
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginVo.getUsername(), loginVo.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authentication);
        SecurityContext context = SecurityContextHolder.getContext();
        context.setAuthentication(authentication);
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        return new ResponseData().setData(loginUser.getToken());
    }

    /**
     * 获取验证码
     *
     * @return
     */
    @SneakyThrows
    @Override
    public ResponseData getVerifyCode() {
        // 生成随机字串
        String code = VerifyCodeUtils.generateVerifyCode(4);
        // 唯一标识
        String uuid = UUIDUtils.getId();

        VerifyCode verifyCode = new VerifyCode();
        verifyCode.setId(uuid);
        verifyCode.setCode(code);
        verifyCodeRepository.save(verifyCode);
        // 生成图片
        int w = 111, h = 36;
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        VerifyCodeUtils.outputImage(w, h, stream, code);
        verifyCode.setImage(Base64.getEncoder().encodeToString(stream.toByteArray()));
        return new ResponseData(verifyCode);
    }

    @Override
    public ResponseData<List<SysMenuVo>> queryPerms() {
        LoginUser loginUser = LoginContextHolder.getLoginUserOrElseThrow();
        Optional<SysUser> optional = sysUserDao.selectById(loginUser.getId());
        if (!optional.isPresent()) {
            throw new CustomException("用户不存在");
        }
        JExample<SysMenu> example = new JExample();
        example.orderBy().asc("parent").asc("orderNum");
        example.createCriteria().andIn("sysRoles", optional.get().getSysRoles());
        List<SysMenuVo> menus = menuDao.selectAll(example, SysMenuVo.class);
        return new ResponseData(menus);
    }

    @Override
    public ResponseData updatePassword(String oldPassword, String newPassword) {
        LoginUser loginUser = LoginContextHolder.getLoginUserOrElseThrow();
        Optional<SysUser> optional = sysUserDao.selectById(loginUser.getId());
        if (!optional.isPresent()) {
            throw new CustomException("用户不存在");
        }
        SysUser sysUser = optional.get();
        if (!encoder.matches(oldPassword, sysUser.getPassword())) {
            throw new CustomException("旧密码不一致！");
        }
        sysUser.setPassword(encoder.encode(newPassword));
        sysUserDao.save(sysUser);
        loginUserRepository.deleteById(sysUser.getId());
        return new ResponseData();
    }

    @Override
    public ResponseData resetPassword(Long id) {
        Optional<SysUser> optional = sysUserDao.selectById(id);
        if (optional.isPresent()) {
            SysUser sysUser = optional.get();
            sysUser.setPassword(encoder.encode(DigestUtils.md5Hex("Aa123456")));
            sysUserDao.save(sysUser);
        } else {
            throw new CustomException("用户不存在");
        }
        return new ResponseData("Aa123456");
    }
}
